package mn.more.mock.io;

import java.util.HashMap;
import java.util.Map;

/**
 * object representation of the persisted mock data.  Please note that this class currently supports only primitive,
 * String and {@link mn.more.mock.io.MockModel}.  Support for other types might be supported in the future.  The reason
 * for not supporting java.lang.Object is because of the problem regarding the data serialization.  With complex or
 * custom types, data serialization and deserialization can be tricky and error-prone.  In order that this class can be
 * used generically, this class now decidedly not to support other types.
 *
 * @author <a href="mailto:mike.liu@aptechmongolia.edu.mn">Mike Liu</a>
 * @version $Id: MockModel.java 5 2008-09-01 12:08:42Z mikeliucc $
 */
public class MockModel {

	private String id;
	private Map<String, Object> properties;

	public MockModel(String id) {
		this.id = id;
		properties = new HashMap<String, Object>();
	}

	public String getId() {
		return id;
	}

	public boolean getBoolean(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Boolean)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type boolean.");
		}

		return (Boolean) value;
	}

	public byte getByte(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Byte)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type byte.");
		}

		return (Byte) value;
	}

	public short getShort(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Short)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type short.");
		}

		return (Short) value;
	}

	public char getChar(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Character)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type char.");
		}

		return (Character) value;
	}

	public int getInt(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Integer)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type int.");
		}

		return (Integer) value;
	}

	public long getLong(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Long)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type long.");
		}

		return (Long) value;
	}

	public float getFloat(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Float)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type float.");
		}

		return (Float) value;
	}

	public double getDouble(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof Double)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type double.");
		}

		return (Double) value;
	}

	public String getString(String property) {
		Object value = properties.get(property);
		if (value == null) {
			throw new NullPointerException("property '" + property + "' does not exist!");
		}

		if (!(value instanceof String)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type String.");
		}

		return (String) value;
	}

	public MockModel getRefModel(String property) {
		Object value = properties.get(property);
		if (value == null) { return null; }

		// (Mike Liu, 2008.05.24): it's better to just return null and let the caller handle this problem.
		// Throwing NPE seems a bit too "impolite".
		//throw new NullPointerException("property '" + property +
		//                               "' does not exist!");
		//}

		if (!(value instanceof MockModel)) {
			throw new ClassCastException("property '" + property + "' does not contain any value of type MockModel.");
		}

		return (MockModel) value;
	}

	public Object getValue(String property) {
		return properties.get(property);
	}

	public boolean[] getBooleanArray(String property) {
		return (boolean[]) getArray(property, boolean.class, null);
	}

	public byte[] getByteArray(String property) {
		return (byte[]) getArray(property, byte.class, null);
	}

	public short[] getShortArray(String property) {
		return (short[]) getArray(property, short.class, null);
	}

	public char[] getCharArray(String property) {
		return (char[]) getArray(property, char.class, null);
	}

	public int[] getIntArray(String property) {
		return (int[]) getArray(property, int.class, null);
	}

	public long[] getLongArray(String property) {
		return (long[]) getArray(property, long.class, null);
	}

	public float[] getFloatArray(String property) {
		return (float[]) getArray(property, float.class, null);
	}

	public double[] getDoubleArray(String property) {
		return (double[]) getArray(property, double.class, null);
	}

	public String[] getStringArray(String property) {
		return (String[]) getArray(property, String.class, null);
	}

	public MockModel[] getRefModelArray(String property) {
		return (MockModel[]) getArray(property, MockModel.class, null);
	}

	public Object[] getValueArray(String property) {
		return (Object[]) getArray(property, Object.class, null);
	}

	private Object getArray(String property, Class type, Object defaultValue) {
		Object value = properties.get(property);
		if (!value.getClass().isArray()) {
			throw new ClassCastException("expect array value for property '" + property + "'");
		}
		return value;
	}

	public void setValue(String property, String value) {
		properties.put(property, value);
	}

	public void setValue(String property, MockModel value) {
		properties.put(property, value);
	}

	public void setValue(String property, int value) {
		properties.put(property, value);
	}

	public void setValue(String property, boolean value) {
		properties.put(property, value);
	}

	public void setValue(String property, char value) {
		properties.put(property, value);
	}

	public void setValue(String property, short value) {
		properties.put(property, value);
	}

	public void setValue(String property, long value) {
		properties.put(property, value);
	}

	public void setValue(String property, float value) {
		properties.put(property, value);
	}

	public void setValue(String property, double value) {
		properties.put(property, value);
	}

	public void setValue(String property, String[] value) {
		setValue(property, value, null);
	}

	public void setValue(String property, MockModel[] value) {
		setValue(property, value, null);
	}

	public void setValue(String property, int[] value) {
		setValue(property, value, "Int");
	}

	public void setValue(String property, boolean[] value) {
		setValue(property, value, "Boolean");
	}

	public void setValue(String property, byte[] value) {
		setValue(property, value, "Byte");
	}

	public void setValue(String property, char[] value) {
		setValue(property, value, "Char");
	}

	public void setValue(String property, short[] value) {
		setValue(property, value, "Short");
	}

	public void setValue(String property, long[] value) {
		setValue(property, value, "Long");
	}

	public void setValue(String property, float[] value) {
		setValue(property, value, "Float");
	}

	public void setValue(String property, double[] value) {
		setValue(property, value, "Double");
	}

	private void setValue(String property, Object array, String type) {
		properties.put(property, array);
	}

	public String[] getPropertyNames() {
		return properties.keySet().toArray(new String[properties.keySet().size()]);
	}

	public Class getPropertyType(String property) {
		Object value = properties.get(property);
		if (value == null) {
			return null;
		}
		return value.getClass();
	}

	public String toString() {
		StringBuilder buffer = new StringBuilder("ID=" + id + "[");
		for (String propName : properties.keySet()) {
			buffer.append(propName).append("=").append(properties.get(propName)).append(";");
		}
		buffer.append("]");
		return buffer.toString();
	}
}

